package com.bt.mes.common.core.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

import javax.annotation.Resource;

import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import com.bt.mes.common.bean.entity.UUIDObject;
import com.bt.mes.common.core.context.AppContext;
import com.bt.mes.common.core.dao.GenericDao;
import com.bt.mes.common.core.history.service.impl.SendHistoryService;
import com.bt.mes.common.util.GUIDGenerator;
import com.bt.mes.common.util.LocalUtil;
import com.bt.mes.common.util.Util;

@Repository
public class GenericHibernateDao<T extends Serializable, PK extends Serializable> extends HibernateDaoSupport
		implements GenericDao<T, PK> {


	@Resource
	private SendHistoryService sendHistoryService;

	private static final String OPERATION_UPDATE = "UPDATE";
	private static final String OPERATION_INSERT = "INSERT";
	private static final String OPERATION_DELETE = "DELETE";
	private static final String OPERATION_SAVE_OR_UPDATE = "IorU";
	private static final String OPERATION_SAVE_OR_UPDATE_ALL = "IorUAL";

	private Class<T> entityClass;
	private SessionFactory sessionFacotry;

	@Resource
	public void setSessionFactoryOverride(SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}

	public GenericHibernateDao() {
		this.entityClass = null;
		Class c = getClass();
		Type t = c.getGenericSuperclass();
		if (t instanceof ParameterizedType) {
			Type[] p = ((ParameterizedType) t).getActualTypeArguments();
			this.entityClass = (Class<T>) p[0];
		}
	}

	public T get(PK id) {
		if (id == null) {
			return null;
		}
		return (T) getHibernateTemplate().get(entityClass, id);
	}

	public T getWithLock(PK id, LockMode lock) {
		if (id == null) {
			return null;
		}
		T t = (T) getHibernateTemplate().get(entityClass, id, lock);
		if (t != null) {
			this.flush(); // 立即刷新，否则锁不会生效。
		}
		return t;
	}

	public T load(PK id) {
		return (T) getHibernateTemplate().load(entityClass, id);
	}

	public T loadWithLock(PK id, LockMode lock) {
		T t = (T) getHibernateTemplate().load(entityClass, id, lock);
		if (t != null) {
			this.flush(); // 立即刷新，否则锁不会生效。
		}
		return t;
	}

	public List<T> loadAll() {
		return (List<T>) getHibernateTemplate().loadAll(entityClass);
	}

	private void setEntityCommonProperty(T entity, String operation) {
		((UUIDObject) entity).setPv_unq_seq_id(((UUIDObject) entity).getUnq_seq_id());
		((UUIDObject) entity).setUnq_seq_id(GUIDGenerator.javaGUID());
		((UUIDObject) entity).setOperation(operation);
		((UUIDObject) entity).setOpe_tbl_name(entity.getClass().getSimpleName().toUpperCase());
		((UUIDObject) entity).setSta_time_d(Util.getDoubleTime());
		((UUIDObject) entity).setOpe_evt_name(AppContext.getCurrServiceName());
		((UUIDObject) entity).setOpe_evt_node(LocalUtil.getLocalInfo().getIp());
		((UUIDObject) entity).setOpe_evt_usr(LocalUtil.getLocalInfo().getHostName());
		((UUIDObject) entity).setOpe_evt_no(AppContext.getCurrEventNumber());
	}

	public void update(T entity) {
		setEntityCommonProperty(entity, OPERATION_UPDATE);
		getHibernateTemplate().update(entity);
		flushCurSession();
		sendHistoryService.sendHistoryMessageToThreadAppContext(entity);
	}

	public void save(T entity) {
		setEntityCommonProperty(entity, OPERATION_INSERT);
		getHibernateTemplate().save(entity);
		flushCurSession();
		sendHistoryService.sendHistoryMessageToThreadAppContext(entity);
	}

	public void saveOrUpdate(T entity) {
		setEntityCommonProperty(entity, OPERATION_SAVE_OR_UPDATE);
		getHibernateTemplate().saveOrUpdate(entity);
		flushCurSession();
		sendHistoryService.sendHistoryMessageToThreadAppContext(entity);
	}

	public void saveOrUpdateAll(Collection<T> entities) {
		getHibernateTemplate().saveOrUpdateAll(entities);
		flushCurSession();
		for (T entity : entities) {
			setEntityCommonProperty(entity, OPERATION_SAVE_OR_UPDATE_ALL);
			sendHistoryService.sendHistoryMessageToThreadAppContext(entity);
		}
	}

	public void delete(T entity) {
		setEntityCommonProperty(entity, OPERATION_DELETE);
		getHibernateTemplate().delete(entity);
		flushCurSession();
		sendHistoryService.sendHistoryMessageToThreadAppContext(entity);
	}

	public void deleteByKey(PK id) {
		this.delete(this.load(id));
	}

	public void deleteAll(Collection<T> entities) {
		getHibernateTemplate().deleteAll(entities);
		flushCurSession();
		for (T entity : entities) {
			setEntityCommonProperty(entity, OPERATION_DELETE);
			sendHistoryService.sendHistoryMessageToThreadAppContext(entity);
		}
	}

	public List<T> find(String queryString) {
		return getHibernateTemplate().find(queryString);
	}

	@SuppressWarnings("rawtypes")
	public List<T> find(String queryString, Object values) {
		return getHibernateTemplate().find(queryString, values);
	}

	@SuppressWarnings("rawtypes")
	public List find(String queryString, Object[] values) {
		return getHibernateTemplate().find(queryString, values);
	}

	@SuppressWarnings("unchecked")
	public List<T> findByNamedParam(String queryString, String paramNames, Object values) {
		return getHibernateTemplate().findByNamedParam(queryString, paramNames, values);
	}

	public void lock(T entity, LockMode lock) {
		getHibernateTemplate().lock(entity, lock);
	}

	public void flush() {
		getHibernateTemplate().flush();
	}

	public List listBySQL(String sql) {
		Session session = getCrSession();
		return session.createSQLQuery(sql).list();
	}

	public List findBySQL(String sql) {
		Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
		Query q = s.createSQLQuery(sql);
		return q.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findBySQL(String sql, Class clazz) {
		Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
		Query q = s.createSQLQuery(sql).addEntity(clazz);
		return q.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findBySQL(String sql, Class clazz, Object... args) {
		Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
		Query q = s.createSQLQuery(sql).addEntity(clazz);
		return q.list();
	}

	public T findBySQLOnly(String sql) {
		Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
		Query q = s.createSQLQuery(sql);
		return (T) q.uniqueResult();
	}

	public List<String> findDistStringList(final String strSql) {
		Query query = getCrSession().createQuery(strSql);
		return query.list();
	}

	public T comTblRead(String sql) {
		Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
		Query q = s.createQuery(sql);
		Object obj = q.uniqueResult();
		return (T) obj;
	}

	public T comTblReadLock(String sql, String tbl_name) {
		Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
		Query q = s.createQuery(sql);
		q.setLockMode(tbl_name, LockMode.READ);
		Object obj = q.uniqueResult();
		return (T) obj;
	}

	public List comTblRead2nd(String sql) {
		Session s = this.getHibernateTemplate().getSessionFactory().getCurrentSession();
		Query q = s.createQuery(sql);
		return q.list();
	}

	public T findOnlyOne(String queryString, Object[] values) {
		return ((List<T>) getHibernateTemplate().find(queryString, values)).get(0);
	}

	public Session getCrSession() {
		return getHibernateTemplate().getSessionFactory().getCurrentSession();
	}

	public void flushCurSession() {
		Session curSession = this.getCrSession();
		curSession.flush();
	}

	public long getCountBySql(String sql) {
		return (Long) getHibernateTemplate().iterate(sql).next();
	}

	@SuppressWarnings("unchecked")
	public List<T> list(String hql, Object... values) {
		Query query = this.createQuery(hql, values);
		return query.list();
	}

	@SuppressWarnings("unchecked")
	public T uniqueResult(String hql, Object... values) {
		Query query = this.createQuery(hql, values);
		return (T) query.uniqueResult();
	}

	protected Query createQuery(String hql, Object... values) {
		Query query = getCrSession().createQuery(hql);

		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query;
	}

	@SuppressWarnings("unchecked")
	public List<T> listWithLock(String hql, Object... values) {
		Session session = this.getCrSession();
		Query query = this.createQuery(hql, values);
		List<T> ts = query.list();
		for (T t : ts) {
			session.lock(t, LockMode.UPGRADE);
		}
		return ts;
	}

	@SuppressWarnings("unchecked")
	public T uniqueResultWithLock(String hql, Object... values) {
		Session session = this.getCrSession();
		Query query = this.createQuery(hql, values);
		T t = (T) query.uniqueResult();
		if (t == null) {
			return t;
		}
		session.lock(t, LockMode.UPGRADE);
		return t;
	}

}
